package com.kinyx.framework.db.daos;

import java.sql.Connection;
import java.util.List;

import com.kinyx.framework.annotation.assist.CanNull;
import com.kinyx.framework.annotation.assist.ReturnNotNull;
import com.kinyx.framework.bean.StringFormatter;
import com.kinyx.framework.bean.define.MethodParam;
import com.kinyx.framework.db.configs.DBConfigs;
import com.kinyx.framework.db.datasource.Datasource;
import com.kinyx.framework.log.Log;
import com.kinyx.framework.utils.KListUtils;

import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;

/**
 * 数据访问参数
 */
public class DaoParam extends MethodParam {

	private DaoParam() { super(null); }
	public DaoParam(@CanNull final Log logger) { super(logger); }

	/** 语句类型 */
	DMLType type;

	/**
	 * 新建或重置参数对象
	 * @param dp 原参数对象
	 * @param type 语句类型
	 * @return 原参数对象或新参数对象
	 */
	@ReturnNotNull
	static DaoParam of(@CanNull final DaoParam dp, final DMLType type) {
		// 未传入参数对象，则创建新参数对象。
		if (dp == null) { return of(new DaoParam(), type); }

		// 统一参数初始化
		dp.init();

		// 自定义参数初始化
		dp.type = type;

		if (dp.transaction == null) {
			if (type.isChange() || type.isBatchChange()) {
				dp.transaction = true;
			} else {
				dp.transaction = false;
			}
		}

		// 返回参数对象
		return dp;
	}

	/** SQL语句 */
	String sql;
	/**
	 * 设置SQL语句
	 * @param sql SQL语句
	 * @return 本对象
	 */
	DaoParam sql(final String sql) {
		this.sql = sql;
		return this;
	}
	/** 参数 */
	List<Object> params;
	/**
	 * 设置参数
	 * @param params 参数
	 * @return 本对象
	 */
	DaoParam params(final List<Object> params) {
		this.params = params;
		return this;
	}
	/** 参数 */
	List<List<Object>> listParams;
	/**
	 * 设置参数
	 * @param listParams 参数
	 * @return 本对象
	 */
	DaoParam listParams(final List<List<Object>> listParams) {
		this.listParams = listParams;
		return this;
	}

	/** 数据源 */
	DBConfigs configs;
	/** 数据源 */
	Datasource datasource;
	/** 数据库访问自定义参数 */
	DaoConfigs daoConfigs;
	/**
	 * 设置数据库配置
	 * @param configs 数据库配置
	 * @return 本对象
	 */
	DaoParam configs(final DBConfigs configs) {
		if (this.configs == null) {
			this.configs = configs;
			this.datasource = configs.getDatasource();
			this.daoConfigs = configs.getDaoConfigs();
		}
		return this;
	}

	/** true：开启事务；false：不开启事务； */
	private Boolean transaction;
	boolean isTransaction() { return Boolean.TRUE.equals(this.transaction); }
	/**
	 * 设置事务（查询语句默认不开启事务，增删改语句默认开启事务。）
	 * @param transaction true：开启事务；false：不开启事务；
	 * @return 本对象
	 */
	public DaoParam transaction(final boolean transaction) {
		this.transaction = transaction;
		return this;
	}
	/** 连接 */
	@Getter(value = AccessLevel.PACKAGE)
	@Setter(value = AccessLevel.PACKAGE)
	private Connection connection;
	/**
	 * 新建子对象
	 * @param type 语句类型
	 * @param sql 语句
	 * @param params 参数
	 * @return 新对象
	 */
	DaoParam subParam(final DMLType type, final String sql, final List<Object> params) {
		// 创建新参数对象
		final DaoParam dp = new DaoParam();

		// 自定义参数初始化
		if (type == null) {
			dp.type = type;
		} else {
			switch (type) {
				case batchInsert:
					dp.type = DMLType.insert;
					break;
				case batchUpdate:
					dp.type = DMLType.update;
					break;
				case batchDelete:
					dp.type = DMLType.delete;
					break;
				default :
					dp.type = type;
					break;
			}
		}

		dp.sql = sql;
		dp.params = params;
		dp.listParams = null;

		dp.configs = this.configs;
		dp.datasource = this.datasource;
		dp.daoConfigs = this.daoConfigs;

		dp.transaction = this.transaction;

		dp.connection = this.connection;

		// 统一参数初始化
		this.initSubParam(dp);

		// 返回参数对象
		return dp;
	}

	// WARN级别以下日志，需要根据配置来进一步判断是否可用。
	private boolean checkShow() {
		if (this.type.isChange()) { return this.daoConfigs.isShowChangeLog(); }
		if (this.type.isBatchChange()) { return this.daoConfigs.isShowChangeLog(); }
		if (this.type.isQuery()) { return this.daoConfigs.isShowQueryLog(); }
		return false;
	}
	@Override
	public boolean isTraceEnabled() { return super.isTraceEnabled() && this.checkShow(); }
	@Override
	public boolean isDebugEnabled() { return super.isDebugEnabled() && this.checkShow(); }
	@Override
	public boolean isInfoEnabled() { return super.isInfoEnabled() && this.checkShow(); }
	private boolean trace;
	public void traceMessage(final Object message) {
		this.trace = true;
		super.trace(message);
		this.trace = false;
	}

	@Override
	protected void logBeforeExecutionImpl() {
		if (this.isTraceEnabled()) { this.trace(null); }
	}

	@Override
	protected void logAfterExecutionImpl() {
		if (this.type.isChange()) {
			if (this.elapsedTime > this.daoConfigs.getSlowChangeTime()) {
				this.warn(null);
			} else {
				if (this.isInfoEnabled()) { this.info(null); }
			}
			return;
		}
		if (this.type.isBatchChange()) {
			if (this.elapsedTime > this.daoConfigs.getSlowBatchChangeTime()) {
				this.warn(null);
			} else {
				if (this.isInfoEnabled()) { this.info(null); }
			}
			return;
		}
		if (this.type.isQuery()) {
			if (this.elapsedTime > this.daoConfigs.getSlowQueryTime()) {
				this.warn(null);
			} else {
				if (this.isDebugEnabled()) { this.debug(null); }
			}
			return;
		}
	}

	@Override
	protected void contentsImplBefore(final StringFormatter sf) {
		if (this.main == null) {
			sf//
					.x("[%s]", this.type)//
			;
		} else {
			sf//
					.x("[%s-%s]", ((DaoParam) this.main).type, this.type)//
			;
		}
	}
	@Override
	protected void contentsImplAfter(final StringFormatter sf) {
		if (this.trace) { return; }
		sf//
				.x(" SQL[%s]", () -> { return this.sql != null; }, () -> { return this.sql; })//
				.x(" PARAMS%s", () -> { return this.params != null; }, () -> { return KListUtils.toString(this.params); })//
		;
	}

}
